package org.someone.office;

import org.apache.poi.hwpf.HWPFDocument;
import org.apache.poi.hwpf.converter.PicturesManager;
import org.apache.poi.hwpf.converter.WordToHtmlConverter;
import org.apache.poi.hwpf.extractor.WordExtractor;
import org.apache.poi.hwpf.usermodel.*;
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
import org.apache.poi.openxml4j.exceptions.OpenXML4JException;
import org.apache.poi.poifs.filesystem.OfficeXmlFileException;
import org.apache.poi.poifs.filesystem.POIFSFileSystem;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.xddf.usermodel.chart.XDDFCategoryDataSource;
import org.apache.poi.xddf.usermodel.chart.XDDFChartData;
import org.apache.poi.xddf.usermodel.chart.XDDFDataSourcesFactory;
import org.apache.poi.xddf.usermodel.chart.XDDFNumericalDataSource;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.apache.poi.xwpf.extractor.XWPFWordExtractor;
import org.apache.poi.xwpf.usermodel.*;
import org.apache.xmlbeans.XmlException;
import org.apache.xmlbeans.XmlOptions;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTBody;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTDocument1;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTP;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTPageMar;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTPageSz;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTR;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTSectPr;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTblWidth;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTc;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTcPr;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.STMerge;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.STPageOrientation;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.STTblWidth;

import fr.opensagres.poi.xwpf.converter.pdf.PdfConverter;
import fr.opensagres.poi.xwpf.converter.pdf.PdfOptions;

import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.*;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import java.io.*;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.*;

public class WordUtil {
	
	public static final BigInteger iwidth_A4_INT_PORTRAIT = BigInteger.valueOf(11907L);//592*20,页面宽度
	public static final BigInteger iheight_A4_INT_PORTRAIT = BigInteger.valueOf(16840L);//842*20,页面高度
	public static final BigInteger iwidth_A3_INT_PORTRAIT = BigInteger.valueOf(16840L);//842*20,页面宽度
	public static final BigInteger iheight_A3_INT_PORTRAIT = BigInteger.valueOf(23820L);//1191*20,页面高度
	
	/**
	 * 自动循环table的列进行自动合并
	 * @param xwpfTable4loop	tables
	 * @param isextendspercol	是否考虑前一列的合并情况
	 */
	public static void autoMergeRow4Tables(List<XWPFTable> xwpfTable4loop, boolean isextendspercol){
		for(XWPFTable table : xwpfTable4loop){
			List<XWPFTableRow> listrow = table.getRows();
			Map<Integer, String> lastTextMap = new HashMap<Integer, String>();
			for(int irow=0; irow< listrow.size(); irow++){
				XWPFTableRow currow = listrow.get(irow);
				for(int icol=0; icol< currow.getTableCells().size(); icol++){
					boolean isneedmerge = false;
					int imergeST = -1;
					XWPFTableCell nextrowcell = null;
					if(irow+1 < listrow.size()){
						XWPFTableRow nextrow = listrow.get(irow+1);
						nextrowcell = nextrow.getCell(icol);
					}
					XWPFTableCell curcell = currow.getCell(icol);
					//如果上一行不存在或者上一行的text不等于本行text则是合并起始行，否则是合并中行
					if(lastTextMap.get(icol) == null){
						imergeST = STMerge.INT_RESTART;
					}else{
						if(curcell.getText().equals(lastTextMap.get(icol))){
							imergeST = STMerge.INT_CONTINUE;
							if(isextendspercol && icol>0){
								XWPFTableCell percolcell = currow.getCell(icol-1);
								if(percolcell.getCTTc().getTcPr()!=null){
									if(percolcell.getCTTc().getTcPr().getVMerge() != null){
										if(STMerge.RESTART.equals(percolcell.getCTTc().getTcPr().getVMerge().getVal()))
											imergeST = STMerge.INT_RESTART;
									}else{
										imergeST = STMerge.INT_RESTART;
									}
								}else{
									imergeST = STMerge.INT_RESTART;
								}
							}
							if(imergeST == STMerge.INT_CONTINUE)isneedmerge = true;
						}else{
							imergeST = STMerge.INT_RESTART;
						}
					}
					lastTextMap.put(icol, curcell.getText());
					if(nextrowcell!= null && curcell.getText().equals(nextrowcell.getText())){
						isneedmerge = true;
					}
					if(isneedmerge){
						if(curcell.getCTTc().getTcPr() != null){
							curcell.getCTTc().getTcPr().addNewVMerge().setVal(imergeST==STMerge.INT_RESTART?STMerge.RESTART:STMerge.CONTINUE);
						}else{
							curcell.getCTTc().addNewTcPr().addNewVMerge().setVal(imergeST==STMerge.INT_RESTART?STMerge.RESTART:STMerge.CONTINUE);
						}
						if(imergeST==STMerge.INT_CONTINUE){
							for(CTP ctp : curcell.getCTTc().getPList()){
								for(CTR ctr:ctp.getRList()){
									if(ctr.getTList()!=null){
										ctr.getTList().clear();
									}
//									ctr.removeT(0);
								}
							}
						}
					}
				}
			}
		}
	}
	
	/**
     * copy word 中tablecell
     * @param fromcell
     * @param tocell
     */
    public static void copyTableCell(XWPFTableCell fromcell, XWPFTableCell tocell){
        CTTc fromcttc = fromcell.getCTTc();
        CTTcPr fromctPr=fromcttc.getTcPr();
        CTTc tocttc = tocell.getCTTc();
        CTTcPr toctPr = tocttc.addNewTcPr();
        XWPFParagraph fromP=fromcell.getParagraphs().get(0);
        XWPFParagraph toP=tocell.getParagraphs().get(0);
        XWPFRun fromRun = fromP.getRuns()!=null&&fromP.getRuns().size()>0?fromP.getRuns().get(0):null;
        XWPFRun toRun = toP.createRun();
        if(fromcell.getColor()!=null)tocell.setColor(fromcell.getColor());
//        if(fromcell.getVerticalAlignment()!=null)tocell.setVerticalAlignment(fromcell.getVerticalAlignment());
        if(fromcttc.getPList().size()>0){
            CTP ctp=fromcttc.getPList().get(0);
            if(ctp.getPPr()!=null){
                if(ctp.getPPr().getJc()!=null){
                    tocttc.getPList().get(0).addNewPPr().addNewJc().setVal(ctp.getPPr().getJc().getVal());
                }
            }
        }
        if(fromctPr.getTcW()!=null){
            toctPr.addNewTcW().setW(fromctPr.getTcW().getW());
        }
        if(fromctPr.getVAlign()!=null){
            toctPr.addNewVAlign().setVal(fromctPr.getVAlign().getVal());
        }
        if(fromctPr.getTcBorders()!=null){
            toctPr.setTcBorders(fromctPr.getTcBorders());
        }
        tocttc.setTcPr(fromctPr);
        if(fromRun != null){
            toRun.getCTR().set(fromRun.getCTR());
        }
    }

	/**
	 * 复制tables行
	 * @param table
	 * @param row
	 * @param length
	 * @param pos
	 */
	public static void copyTableRow(XWPFTable table, XWPFTableRow row, int length, int pos){
	    for(int i=0; i< length; i++){
	        XWPFTableRow newrow = table.insertNewTableRow(pos);
	        newrow.setHeight(row.getHeight());
	        for(int m=0; m< row.getTableCells().size(); m++){
	            XWPFTableCell newcell = newrow.addNewTableCell();
	            XWPFTableCell oldcell = row.getCell(m);
	            copyTableCell(oldcell, newcell);
			}
	    }
	}

	/**
	 * 注：不支持图片，支持表格
	 * @param wordDocument
	 * @return
	 * @throws TransformerException
	 * @throws IOException
	 * @throws ParserConfigurationException
	 * @throws OfficeXmlFileException
	 */
	public static String doc2Html(HWPFDocument wordDocument) throws ParserConfigurationException, IOException, TransformerException {
	    WordToHtmlConverter wordToHtmlConverter = new WordToHtmlConverter(
	                DocumentBuilderFactory.newInstance().newDocumentBuilder()
	                        .newDocument());
	    List<Picture> pics = wordDocument.getPicturesTable().getAllPictures();
	    for (int i = 0; i < pics.size(); i++) {
	        Picture pic = (Picture) pics.get(i);
	
	        pic.writeImageContent(new FileOutputStream("F://"
	                + pic.suggestFullFileName()));
	    }
	
	        wordToHtmlConverter.setPicturesManager(new PicturesManager() {
	            public String savePicture(byte[] content,
	                                      PictureType pictureType, String suggestedName,
	                                      float widthInches, float heightInches) {
	                try (FileOutputStream out = new FileOutputStream("F://" + suggestedName)) {
	                        out.write(content);
	                } catch (Exception e) {
	                    e.printStackTrace();
	                }
	                return "F://" + suggestedName;
	            }
	        });
	        wordToHtmlConverter.processDocument(wordDocument);
	        // save pictures
	        /*List pics = wordDocument.getPicturesTable().getAllPictures();
	        if (pics != null) {
	            for (int i = 0; i < pics.size(); i++) {
	                Picture pic = (Picture) pics.get(i);
	                pic.writeImageContent(new FileOutputStream("F://"
	                        + pic.suggestFullFileName()));
	            }
	        }*/
	        org.w3c.dom.Document htmlDocument = wordToHtmlConverter.getDocument();
	        ByteArrayOutputStream out = new ByteArrayOutputStream();
	        DOMSource domSource = new DOMSource(htmlDocument);
	        StreamResult streamResult = new StreamResult(out);
	
	        TransformerFactory tf = TransformerFactory.newInstance();
	        Transformer serializer = tf.newTransformer();
	        serializer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
	        serializer.setOutputProperty(OutputKeys.INDENT, "yes");
	        serializer.setOutputProperty(OutputKeys.METHOD, "html");
	        serializer.transform(domSource, streamResult);
	        out.close();
	        // writeFile(new String(out.toByteArray()), outPutFile);
	        return new String(out.toByteArray());
	}
	
	/**
     * 获取table中自动合并列的多个cell，合并规则是内容一致或值为null
     * @param table
     * @return
     */
    public static Map<String, List<XWPFTableCell>> getAutoMergerColCells(XWPFTable table){
    	Map<String, List<XWPFTableCell>> listmap = new HashMap<String, List<XWPFTableCell>>();
    	List<XWPFTableRow> listrow = table.getRows();
    	for(int irow=0; irow < listrow.size(); irow++){
    		XWPFTableRow row = listrow.get(irow);
    		List<XWPFTableCell> listCells = row.getTableCells();
    		List<XWPFTableCell> celllist = new ArrayList<>();
    		String strkey = null;
    		for(int icol=0; icol<listCells.size(); icol++){
    			XWPFTableCell curcell = listCells.get(icol);
    			XWPFTableCell nextcell = null;
    			if(icol+1<listCells.size()){
    				nextcell = listCells.get(icol+1);
    			}
    			if(icol ==0){
    				strkey = irow + "," + icol;
    			}
    			celllist.add(curcell);
    			boolean isMergerOver = false;
    			//下一单元格为null 或者   下一单元格值与本单元格值不等 
    			if(nextcell == null){
    				isMergerOver = true;
    			}else{
    				if(nextcell.getText() != null && !nextcell.getText().equals(curcell.getText())){
    					isMergerOver = true;
    				}
    			}
    			//如果本单元格为null或本单元格值为null或空串 则合并
				if(curcell == null || curcell.getText() == null || "".equals(curcell.getText())) {
					isMergerOver = true;
				}
    			if(isMergerOver){
    				if(celllist.size()>1){
    					List<XWPFTableCell> tmp = new ArrayList<>();
    					tmp.addAll(celllist);
    					listmap.put(strkey, tmp);
    				}
    				celllist.clear();
    				strkey = irow+","+(icol+1);
    			}
    			
    		}
    	}
    	return listmap;
    }
    
    /**
     * 获取table中自动合并行的多个cell，合并规则是内容一致或值为null
     * @param table
     * @param isextendspercol
     * @return
     */
    public static Map<String, List<XWPFTableCell>> getAutoMergerRowCells(XWPFTable table, boolean isextendspercol){
    	Map<String, List<XWPFTableCell>> listmap = new HashMap<String, List<XWPFTableCell>>();
    	if(table.getCTTbl().getTblGrid()==null){
    		return null;
    	}
    	List<XWPFTableRow> listrow = table.getRows();
    	int icolsize = table.getCTTbl().getTblGrid().getGridColList().size();
    	for(int icol=0; icol<icolsize; icol++){
    		List<XWPFTableCell> celllist = new ArrayList<>();
    		String strkey = null;
    		for(int irow=0; irow< listrow.size(); irow++){
    			XWPFTableRow currow = listrow.get(irow);
    			XWPFTableRow nextrow = null;
    			if(irow+1 < listrow.size()){
    				nextrow = listrow.get(irow+1);
    			}
    			if(irow ==0){
    				strkey = irow+","+icol;
    			}
    			XWPFTableCell curcell = currow.getCell(icol);
    			celllist.add(curcell);
    			boolean isMergerOver = false;
    			//如果下一行为null 或者  如果下一行与本行不等  或者   (考虑前列且下一行的前一列是restart或null) 
    			if(nextrow == null){
    				isMergerOver = true;
    			}else{
    				XWPFTableCell nextcell = nextrow.getCell(icol);
    				if(nextcell.getText() != null && !nextcell.getText().equals(curcell.getText())){
    					isMergerOver = true;
    				}else{
    					if(isextendspercol && icol>0){
    						XWPFTableCell pernextcell = nextrow.getCell(icol-1);
    						if(pernextcell.getCTTc().getTcPr()!=null){
								if(pernextcell.getCTTc().getTcPr().getVMerge() != null){
									if(STMerge.RESTART.equals(pernextcell.getCTTc().getTcPr().getVMerge().getVal()))
										isMergerOver = true;
								}else{
									isMergerOver = true;
								}
							}else{
								isMergerOver = true;
							}
    					}
    				}
    			}
    			if(isMergerOver){
    				if(celllist.size()>1){
    					List<XWPFTableCell> tmp = new ArrayList<>();
    					tmp.addAll(celllist);
    					listmap.put(strkey, tmp);
    				}
    				celllist.clear();
    				strkey = (irow+1)+","+icol;
    			}
    		}
    	}
    	return listmap;
    }

	/**
	 *  获得word2003替换之后的文档文本
	 * @param is    文件输入流
	 * @param map   替换文字的map key是word模板中${...}内的表达式，value是替换后的表达式
	 * @return  返回String txt
	 * @throws IOException  读写错误
	 */
	public static String getReplaceDocTxt(InputStream is, Map<String, Object> map) throws IOException {
	    String strDocTxt = null;
		HWPFDocument doc = replaceDoc(is, map);
	    WordExtractor ex  = new WordExtractor(doc);
	    strDocTxt = ex.getText();
	    ex.close();
	    return strDocTxt;
	}

	/**
	 *
	 * @param is    文件输入流
	 * @param map   替换文字的map key是word模板中${...}内的表达式，value是替换后的表达式
	 * @return  返回String txt
	 * @throws IOException  读写错误
	 * @throws InvalidFormatException 
	 */
	public static String getReplaceDocxTxt(InputStream is, Map<String, Object> map, boolean isAutoMerge) throws IOException, InvalidFormatException {
	    String strDocxTxt = null;
		XWPFDocument docx = replaceDocx(is, map, isAutoMerge);
	    XWPFWordExtractor extractor = new XWPFWordExtractor(docx);
	    strDocxTxt = extractor.getText();
	    extractor.close();
	    return strDocxTxt;
	}

	/**
	 * 获取循环变量的tables
	 * @param xwpfTableList	需要进行判断的tables
	 * @param map	变量表
	 * @return
	 */
	public static List<XWPFTable> getTables4Loop(List<XWPFTable> xwpfTableList, Map<String,Object> map){
		List<XWPFTable> listtables = new ArrayList<XWPFTable>();
		for(XWPFTable table : xwpfTableList){
			String strcontent = table.getRow(0).getCell(0).getText();
	        String strval = VarUtil.getVar(strcontent, VarUtil.defaultRegex);
	        if(strval != null){
	            strval = strval.substring(2,strval.length()-1);
	            Object obj = map.get(strval);
	            if(obj!=null && obj instanceof List){
	            	listtables.add(table);
	            }
	        }
		}
		return listtables;
	}

	/**
	 * 是否是table中循环的行
	 * @param row
	 * @return
	 */
	public static boolean isLoopline(XWPFTableRow row){
	    List<XWPFTableCell> cells = row.getTableCells();
	    for (XWPFTableCell cell : cells) {
	        List<XWPFParagraph> paragraphListTable = cell.getParagraphs();
	        for(XWPFParagraph paragraph:paragraphListTable){
	            String ptext = paragraph.getParagraphText();
	            if(ptext.length()==0){
	                continue;
	            }
	            if(VarUtil.isVar(ptext, VarUtil.defaultRegex)){
	                return true;
	            }
	        }
	    }
	    return false;
	}
	
	/**
     * 多列合并
     * @param cellList
     */
    public static void mergerColCell(List<XWPFTableCell> cellList){
    	if(cellList == null || cellList.size()==0){
    		return; 
    	}
    	int index = 0;
    	int ispan = cellList.size();
    	int iwidth = 0;
    	for(XWPFTableCell cell: cellList){
    		iwidth += cell.getWidth();
    	}
    	for(XWPFTableCell cell: cellList){
    		CTTcPr ctpr = cell.getCTTc().getTcPr();
    		if(ctpr == null){
    			cell.getCTTc().addNewTcPr();
    		}
    		if(index++ == 0){
    			ctpr.getTcW().setW(BigInteger.valueOf(iwidth));
    			ctpr.addNewGridSpan().setVal(BigInteger.valueOf(ispan));
    		}else{
    			int id = cell.getTableRow().getTableCells().indexOf(cell);
    	    	cell.getTableRow().removeCell(id);
    	    	cell.getTableRow().getCtRow().removeTc(id);
    		}
    	}
    }
    
    /**
     * 多行合并
     * @param cellList
     */
    public static void mergerRowCell(List<XWPFTableCell> cellList){
    	if(cellList == null || cellList.size()==0){
    		return; 
    	}
    	int index = 0;
    	for(XWPFTableCell cell: cellList){
    		CTTcPr ctpr = cell.getCTTc().getTcPr();
    		if(ctpr == null){
    			cell.getCTTc().addNewTcPr();
    		}
    		if(index++ == 0){
    			ctpr.addNewVMerge().setVal(STMerge.RESTART);
    		}else{
    			ctpr.addNewVMerge().setVal(STMerge.CONTINUE);
    			for(CTP ctp : cell.getCTTc().getPList()){
					for(CTR ctr:ctp.getRList()){
						if(ctr.getTList()!=null){
							ctr.getTList().clear();
						}
					}
				}
    		}
    	}
    }

	/**
	 * 替换Chart中的变量
	 * @param xc	chart
	 * @param map	变量表
	 * @throws InvalidFormatException
	 * @throws IOException
	 */
	public static void replaceChart(XWPFChart xc, Map<String, Object> map) throws InvalidFormatException, IOException{
		XSSFWorkbook workbook = xc.getWorkbook();
	    XSSFSheet sheet = workbook.getSheetAt(0);
	    ExcelUtil.replaceSheet(sheet, map);
	    ExcelUtil.replaceSheet4Loop(sheet, map, false);
	    ExcelUtil.parseStr2Db4Sheet(sheet);
	    if(xc!=null){
	    	if(xc.getCTChart()!=null){
	    		CTChartUtil.reloadCTChart(xc.getCTChart(), sheet);
	    	}
	    }
//	    for(XDDFChartData chartData : xc.getChartSeries()){
//	    	replaceChartData(chartData,sheet);
//	    }
	}

	/**
	 * 根据表格内容替换chart默认值中的数据
	 * @param cd	chart
	 * @param sheet	表格
	 */
	public static void replaceChartData(XDDFChartData cd,XSSFSheet sheet){
		int icol = 1;
		for(XDDFChartData.Series series : cd.getSeries()){
			XDDFCategoryDataSource category = null;
			XDDFNumericalDataSource<Double> values = null;
			int firstRow = 1;
			int lastRow = sheet.getLastRowNum();
			CellRangeAddress cregion = new CellRangeAddress(firstRow,lastRow,0,0);
			category = XDDFDataSourcesFactory.fromStringCellRange(sheet, cregion);
			CellRangeAddress vregion = new CellRangeAddress(firstRow,lastRow,icol,icol);
			ExcelUtil.parseStr2Db4Range(sheet, vregion);
			values = XDDFDataSourcesFactory.fromNumericCellRange(sheet, vregion); 
			series.replaceData(category, values);
			series.plot();
			icol++;
		}
	}
	
	/**
	 * 替换图表数据内容变量list
	 * @param xwpfChartList
	 * @param map
	 * @throws InvalidFormatException
	 * @throws IOException
	 */
	public static void replaceChartList(List<XWPFChart> xwpfChartList, Map<String, Object> map) throws InvalidFormatException, IOException{
		for(XWPFChart chart : xwpfChartList){
			replaceChart(chart, map);
		}
	}

	/**
     *
     * @param is   文件输入流
     * @param map   替换文字的map key是word模板中${...}内的表达式，value是替换后的表达式
     * @return  返回HWPFDocument word2003以下版本的处理对象
     * @throws IOException  读写错误
     */
    public static HWPFDocument replaceDoc(InputStream is, Map<String, Object> map) throws IOException {
            POIFSFileSystem fs = new POIFSFileSystem(is);
            HWPFDocument doc = new HWPFDocument(fs);
            //读取页眉
            Range headerRange = doc.getHeaderStoryRange();
            //读取word文本内容
            Range bodyRange = doc.getRange();
//            //获取图片-不过没个鸟用,因为poi 不支持2003以下版本的word写入图片
//            byte[] datastream = doc.getDataStream();
//            int numChar = bodyRange.numCharacterRuns();
//            PicturesTable pt = new PicturesTable(doc,datastream, datastream);
//            for(int i=0; i< numChar ; i++){
//                CharacterRun cr = bodyRange.getCharacterRun(i);
//                if(pt.hasPicture(cr)){
//                    Picture p = pt.extractPicture(cr, true);
//                }
//            }
            //读取页脚
            Range footerRange = doc.getFootnoteRange();
            // 替换文本内容
            //System.out.println(headerRange.text());
            for (Map.Entry<String, Object> entry : map.entrySet()) {
                String key = "${" + entry.getKey() + "}";
                //System.out.println("=============key:"+key+";val:"+entry.getValue());
                String val = entry.getValue()!=null?entry.getValue().toString():"";
//                System.out.println(headerRange.text());
                if(headerRange.text().length()>0 && headerRange.text().indexOf(key)!=-1){
                    headerRange.replaceText(key, val);
                    headerRange = doc.getHeaderStoryRange();
                    bodyRange = doc.getRange();
                    footerRange = doc.getFootnoteRange();
                }
                if(bodyRange.text().length()>0 && bodyRange.text().indexOf(key)!=-1){
                    bodyRange.replaceText(key, val);
                    headerRange = doc.getHeaderStoryRange();
                    bodyRange = doc.getRange();
                    footerRange = doc.getFootnoteRange();
                }
                if(footerRange.text().length()>0 && footerRange.text().indexOf(key)!=-1){
                    footerRange.replaceText(key, val);
                    headerRange = doc.getHeaderStoryRange();
                    bodyRange = doc.getRange();
                    footerRange = doc.getFootnoteRange();
                }
            }
            return doc;
    }

    /**
     *
     * @param is   文件输入流
     * @param map   替换文字的map key是word模板中${...}内的表达式，value是替换后的表达式
     * @return  返回XWPFDocument word2007以上版本的处理对象
     * @throws IOException  读写错误
     * @throws InvalidFormatException 
     * @throws OpenXML4JException 
     */
    public static XWPFDocument replaceDocx(InputStream is, Map<String, Object> map, boolean isAutoMerge) throws IOException, InvalidFormatException {
    	XWPFDocument docx = new XWPFDocument(is);
    	return replaceDocx(docx, map, isAutoMerge);
    }
    
    /**
     * 根据map替换XWPFDocument变量
     * @param docx	XWPFDocument docx
     * @param map	变量库
     * @param isAutoMerge	是否自动合并行
     * @return
     * @throws IOException
     * @throws InvalidFormatException
     */
    public static XWPFDocument replaceDocx(XWPFDocument docx , Map<String, Object> map, boolean isAutoMerge) throws IOException, InvalidFormatException {
    	//读取页眉
        for(int i = 0; i< docx.getHeaderList().size(); i++){
            List<XWPFParagraph> headers = docx.getHeaderList().get(i).getParagraphs();
            replace4Paragraphs(headers, map);
        }

//        //读取页脚
//        for(int i = 0; i< docx.getFooterList().size(); i++){
//            List<XWPFParagraph> headers = docx.getFooterList().get(i).getParagraphs();
//            replace4Paragraphs(headers, map);
//        }

        //读取文档内容
        List<XWPFParagraph> body = docx.getParagraphs();
        replace4Paragraphs(body, map);

        //读取表格内容
        List<XWPFTable> xwpfTableList = docx.getTables();
        //替换表格中的固定变量
        replaceTableList(xwpfTableList, map);
        //获取需要进行变量行循环的表格
        List<XWPFTable> xwpfTable4loop = getTables4Loop(xwpfTableList, map);
        //处理循环表格
        replaceTableListLoop(xwpfTable4loop, map);
        //处理合并行，考虑前一列的合并情况
        if(isAutoMerge)autoMergeRow4Tables(xwpfTable4loop, true);
        //处理图表数据
        replaceChartList(docx.getCharts(), map);
        //处理替换图片
        repalcePicturesList(docx, map);
        
        return docx;
    }

	/**
	 * 
	 * @param in	输入流
	 * @param out	输出流
	 * @param varmap	变量库
	 * @throws IOException
	 * @throws InvalidFormatException 
	 */
	public static void repalceDocx2IO(InputStream in, OutputStream out, Map<String,Object> varmap, boolean isAutoMerge) throws IOException, InvalidFormatException{
		XWPFDocument docx = replaceDocx(in, varmap, isAutoMerge);
		docx.write(out);
		docx.close();
	}
	
	/**
	 * 替换段落list中的图片
	 * @param paragraphlist
	 * @param map
	 * @throws IOException
	 */
	public static void repalcePictures4PHList(List<XWPFParagraph> paragraphlist, Map<String,Object> map) throws IOException{
		for(XWPFParagraph paragraph: paragraphlist){
			for(XWPFRun run :paragraph.getRuns()){
				for(XWPFPicture pic :run.getEmbeddedPictures()){
					System.out.println(pic.getCTPicture().getNvPicPr().getCNvPr().getName());
					Object objpic = map.get(pic.getCTPicture().getNvPicPr().getCNvPr().getName());
					if(objpic != null){
	    				if(objpic instanceof byte[]){
	    					OutputStream os = pic.getPictureData().getPackagePart().getOutputStream();
	    					os.write((byte[])objpic);
	    					pic.getPictureData().getPackagePart().getPackage().save(os);
	    				}
					}
				}
			}
		}
	}
	
	/**
	 * 替换图片
	 * @param paragraphlist
	 * @param map
	 * @throws InvalidFormatException
	 * @throws IOException
	 */
	public static void repalcePicturesList(XWPFDocument docx, Map<String,Object> map) throws IOException{
		//处理替换图片
		repalcePictures4PHList(docx.getParagraphs(), map);
    	
    	for(XWPFTable table :docx.getTables()){
    		for(XWPFTableRow row :table.getRows()){
    			for(XWPFTableCell cell :row.getTableCells()){
    				repalcePictures4PHList(cell.getParagraphs(), map);
    			}
    		}
    	}
	}

	/**
	 * 替换tabls中行
	 * @param table
	 * @param map
	 */
	public static void replaceTable(XWPFTable table, Map<String, Object> map){
	    List<XWPFTableRow> rows = table.getRows();
	    for (XWPFTableRow row : rows) {
	        replaceTableRow(row, map);
	    }
	}

	/**
	 * 替换单元格
	 * @param cell
	 * @param map
	 */
	public static void replaceTableCell(XWPFTableCell cell, Map<String, Object> map){
	    List<XWPFParagraph> paragraphListTable = cell.getParagraphs();
	    replace4Paragraphs(paragraphListTable, map);
	}

	/**
     * 替换tables中的普通变量
     * @param xwpfTableList
     * @param map
     */
    public static void replaceTableList(List<XWPFTable> xwpfTableList, Map<String, Object> map){
        for(XWPFTable table : xwpfTableList){
            replaceTable(table, map);
        }
    }
    
    /**
	 * 替换tables中的需要循环的变量
	 * @param xwpfTableList
	 * @param map
	 */
	public static void replaceTableListLoop(List<XWPFTable> xwpfTableList, Map<String,Object> map){
	    for(XWPFTable table : xwpfTableList){
	        //getlooptablekey
	    	replaceTableLoop(table, map);
	    }
	}

	/**
	 * 替换需循环的表格
	 * @param table	需替换的table
	 * @param map	变量库
	 */
	public static void replaceTableLoop(XWPFTable table, Map<String,Object> map){
		String strcontent = table.getRow(0).getCell(0).getText();
	    String strval = VarUtil.getVar(strcontent, VarUtil.defaultRegex);
	    if(strval != null){
	        strval = strval.substring(2,strval.length()-1);
	        Object obj = map.get(strval);
	        if(obj!=null && obj instanceof List){
	            //将table中的循环变量替换为""
	            Map<String,Object> tmpMap = new HashMap<String,Object>();
	            tmpMap.put(strval,"");
	            replaceTableCell(table.getRow(0).getCell(0), tmpMap);
	
	            List<Map<String,Object>> mapList = (List<Map<String,Object>>)obj;
	            if(mapList!=null && mapList.size()>0){
	                List<XWPFTableRow> rows = table.getRows();
	                int line=0;
	                boolean isLoop = false;
	                for(; line< rows.size(); line++){
	                    XWPFTableRow row = rows.get(line);
	                    if(isLoopline(row)){
	                        copyTableRow(table, row, mapList.size()-1, line);
	                        isLoop = true;
	                        break;
	                    }
	                }
	                if(isLoop) {
	                    for (int i = 0; i < mapList.size(); i++) {
	                        Map<String, Object> varMap = mapList.get(i);
	                        XWPFTableRow row = table.getRow(line + i);
	                        replaceTableRow(row, varMap);
	                    }
	                }
	            }
	        }
	    }
	}

	/**
     * 替换tables row中列的变量
     * @param row
     * @param map
     */
    public static void replaceTableRow(XWPFTableRow row, Map<String, Object> map){
        List<XWPFTableCell> cells = row.getTableCells();
        for (XWPFTableCell cell : cells) {
            replaceTableCell(cell, map);
        }
    }

    /**
     *
     * @param paragraphList     文档段落list
     * @param param     替换文字的map key是word模板中${...}内的表达式，value是替换后的表达式
     */
    private static void replace4Paragraphs(List<XWPFParagraph> paragraphList, Map<String, Object> param){
        if(paragraphList != null && paragraphList.size() > 0){
            for(XWPFParagraph paragraph:paragraphList){
                replace4ParagraphByPH(paragraph, param);
//                replace4ParagraphByRun(paragraph, param);
            }
        }
    }

    /**
     * 替换段落中的文字
     * @param paragraph
     * @param param
     */
    private static void replace4ParagraphByPH(XWPFParagraph paragraph, Map<String, Object> param){
        String ptext = paragraph.getParagraphText();
        if(ptext.length()==0){
            return;
        }
        for(int i = paragraph.getRuns().size()-1; i> 0; i--){
            paragraph.removeRun(i);
        }
        ptext = VarUtil.replaceText(ptext, param);
        //System.out.println(paragraph.getRuns().size());
        if(paragraph.getRuns().size()>0){
            paragraph.getRuns().get(0).setText(ptext,0);
        }
    }

    /**
     * 以Run的方式替换段落
     * @param paragraph
     * @param param
     */
    private static void replace4ParagraphByRun(XWPFParagraph paragraph, Map<String, Object> param){
        List<XWPFRun> runs;
        if(VarUtil.isVar(paragraph.getParagraphText(), VarUtil.defaultRegex)) {
            runs = paragraph.getRuns();
            for (int i = 0; i < runs.size(); i++) {
                XWPFRun run = runs.get(i);
                String runText = run.toString();
                String replaceText = VarUtil.replaceText(runText, param);
                if(replaceText.indexOf("\r")>-1) {
                    String[] text = replaceText.split("\r");
                    paragraph.removeRun(i);
                    for(int f=text.length-1;f>=0;f--) {
//                        System.out.println(f);
                        paragraph.insertNewRun(i).setText(text[f]);
                        if(f!=0) {
//                            paragraph.getDocument().createParagraph();
                            paragraph.insertNewRun(i).addBreak();
                        }
                    }

                }else{
                    paragraph.removeRun(i);
                    paragraph.insertNewRun(i).setText(replaceText);
                }
            }
        }
    }
    
    /**
     * 设置XWPFTableCell 的文字，默认使用table的对应列宽
     * @param cell
     * @param text
     */
    public static void setCellValue(XWPFTableCell cell, String text){
    	setCellValue(cell, text, -1);
    }
    
    /**
     * 设置XWPFTableCell 的文字，默认使用传入的列宽iwidth，如果iwidth<0 则使用table对应的列宽 
     * @param cell
     * @param text
     * @param iwidth
     */
    public static void setCellValue(XWPFTableCell cell, String text, int iwidth){
    	XWPFParagraph ph = null;
    	if(cell.getParagraphs()!=null && cell.getParagraphs().size()>0){
    		ph = cell.getParagraphs().get(0);
    	}else{
    		ph = cell.addParagraph();
    	}
    	ph.setAlignment(ParagraphAlignment.CENTER);
    	XWPFRun run = null;
    	if(ph.getRuns() != null && ph.getRuns().size()>0){
    		run = ph.getRuns().get(0);
    	}else{
    		run = ph.createRun();
    	}
    	run.setFontFamily("宋体");
    	run.setText(text,0);
    	BigInteger icellwidth = BigInteger.ZERO;
    	if(iwidth >= 0){
    		icellwidth = BigInteger.valueOf(iwidth);
    	}else{
    		XWPFTableRow row = cell.getTableRow();
        	XWPFTable table = row.getTable();
        	int id = row.getTableCells().indexOf(cell);
        	if(table.getCTTbl().getTblGrid() != null){
        		if(table.getCTTbl().getTblGrid().getGridColList() != null 
        				&& !table.getCTTbl().getTblGrid().getGridColList().isEmpty()
        				&& id<table.getCTTbl().getTblGrid().getGridColList().size()){
        			icellwidth = table.getCTTbl().getTblGrid().getGridColList().get(id).getW();
        		}
        	}
    	}
    	CTTblWidth ctw = null;
    	if(cell.getCTTc().getTcPr()!=null){
    		if(cell.getCTTc().getTcPr().getTcW()!=null){
    			ctw = cell.getCTTc().getTcPr().getTcW();
    		}else{
    			ctw = cell.getCTTc().getTcPr().addNewTcW();
    		}
    	}else{
    		ctw = cell.getCTTc().addNewTcPr().addNewTcW();
    	}
    	ctw.setW(icellwidth);
    	ctw.setType(STTblWidth.AUTO);
    }
    
    /**
     * 根据宽度，设置doc的纸张样式目前支持（A3，A4）
     * @param docx
     * @param iWidth
     */
    public static BigInteger setPageStyleByWidth(XWPFDocument docx, BigInteger iWidth){
    	BigInteger ipagewidth = BigInteger.valueOf(11907);//592*20,页面宽度
    	BigInteger ipageheight = BigInteger.valueOf(16840);//842*20,页面高度
    	BigInteger ipagetop = BigInteger.valueOf(1620L);	//默认页眉页边距
    	BigInteger ipagebottom = BigInteger.valueOf(1620L);	//默认页脚页边距
    	BigInteger ipageleft = BigInteger.valueOf(1440L);	//默认左侧页边距
    	BigInteger ipageright = BigInteger.valueOf(1440L);	//默认左侧页边距
    	int iorient = STPageOrientation.INT_PORTRAIT;
    	if(iWidth.compareTo(iwidth_A4_INT_PORTRAIT)<=0){ //如果传入宽度小于A4纸纵向的宽度11907则按照A4纵向处理
    		ipagewidth = iwidth_A4_INT_PORTRAIT;
        	ipageheight = iheight_A4_INT_PORTRAIT;
    	}else if(iWidth.compareTo(iheight_A4_INT_PORTRAIT)<=0){ //否则 如果传入宽度小于A4纸高度16840则按照A4横向处理
    		ipagewidth = iheight_A4_INT_PORTRAIT;
        	ipageheight = iwidth_A4_INT_PORTRAIT;
        	iorient = STPageOrientation.INT_LANDSCAPE;
    	}else if(iWidth.compareTo(iheight_A3_INT_PORTRAIT)<=0){ //否则 如果传入宽度小于A3纸高度23820L则按照A3横向处理
    		ipagewidth = iheight_A3_INT_PORTRAIT;
        	ipageheight = iwidth_A3_INT_PORTRAIT;
        	iorient = STPageOrientation.INT_LANDSCAPE;
    	}else{	//异形纸张
    		ipagewidth = iWidth.add(ipageleft).add(ipageright);
    		//设置页面纵横比为 根号2 Math.sqrt(2)
    		BigInteger height = BigDecimal.valueOf(iWidth.doubleValue()).divide(BigDecimal.valueOf(Math.sqrt(2)), 0, BigDecimal.ROUND_HALF_UP).toBigInteger() ;
    		ipageheight = height.add(ipagetop).add(ipagebottom);
    		iorient = STPageOrientation.INT_LANDSCAPE;
    	}
    	if(ipagewidth.compareTo(iWidth)==1){	//如果纸张宽度大于传入宽度 则调整页边距
    		BigInteger iwidthsub = ipagewidth.subtract(iWidth);
        	BigInteger ileftnew = iwidthsub.divide(BigInteger.valueOf(2));
        	if(ipageleft.compareTo(ileftnew)==1){
        		ipageleft = ileftnew;
        	}
        	ipageright = iwidthsub.subtract(ipageleft);
    	}
    	setPageStyle(docx, ipagewidth, ipageheight, ipagetop, ipagebottom, ipageleft, ipageright, iorient);
    	return ipagewidth.subtract(ipageleft).subtract(ipageright);
    }
    
    /**
     * 设置wordpage样式
     * @param docx	word
     * @param iwidth	宽
     * @param iheight	高
     * @param itop		页眉页边距
     * @param ibottom	页脚页边距
     * @param ileft		左侧页边距
     * @param iright	右侧页边距
     * @param iorient	横向纵向
     */
    public static void setPageStyle(XWPFDocument docx, BigInteger iwidth, BigInteger iheight, BigInteger itop, BigInteger ibottom, BigInteger ileft, BigInteger iright, int iorient){
    	CTDocument1 ctdoc = docx.getDocument();
    	if(ctdoc!= null){
    		CTBody ctbody = ctdoc.getBody();
    		if(ctbody == null){
    			ctbody = ctdoc.addNewBody();
    		}
    		CTSectPr ctspr = ctbody.getSectPr();
    		if(ctspr == null){
    			ctspr = ctbody.addNewSectPr();
    		}
    		CTPageSz ctpsz = ctspr.getPgSz();
    		if(ctpsz==null){
    			ctpsz = ctspr.addNewPgSz();
    		}
    		ctpsz.setW(iwidth);
    		ctpsz.setH(iheight);
    		if(iorient == STPageOrientation.INT_PORTRAIT){
    			ctpsz.setOrient(STPageOrientation.PORTRAIT);
    		}else{
    			ctpsz.setOrient(STPageOrientation.LANDSCAPE);
    		}
    		CTPageMar ctpm = ctspr.getPgMar();
    		if(ctpm == null){
    			ctpm = ctspr.addNewPgMar();
    		}
    		ctpm.setTop(itop);
    		ctpm.setBottom(ibottom);
    		ctpm.setLeft(ileft);
    		ctpm.setRight(iright);
    	}
    }

    /**
     * docx 转pdf
     * @param docx
     * @param target
     * @throws IOException
     */
    public static void wordConverterPdf(XWPFDocument docx, OutputStream target) throws IOException {
    	PdfOptions options = PdfOptions.getDefault();//.create();
        PdfConverter.getInstance().convert(docx, target, options);
    }
    
    /**
     * docx 转pdf
     * @param in
     * @param out
     * @throws IOException
     */
    public static void wordConverterPdf(InputStream in, OutputStream out) throws IOException {
    	XWPFDocument docx = new XWPFDocument(in);
    	wordConverterPdf(docx, out);
    	docx.close();
    }
    
    public static void appendWord(XWPFDocument rootpart,XWPFDocument addpart) throws IOException, XmlException {
    	CTBody rootBody = rootpart.getDocument().getBody();
    	CTBody addpartBody = addpart.getDocument().getBody();
    	XmlOptions optionsOuter = new XmlOptions();
	    optionsOuter.setSaveOuter();
	    String appendString = addpartBody.xmlText(optionsOuter);
	    String rootString = rootBody.xmlText();
	    String prefix = rootString.substring(0,rootString.indexOf(">")+1);
	    String mainPart = rootString.substring(rootString.indexOf(">")+1,rootString.lastIndexOf("<"));
	    String sufix = rootString.substring( rootString.lastIndexOf("<") );
	    String addPart = appendString.substring(appendString.indexOf(">") + 1, appendString.lastIndexOf("<"));
	    CTBody makeBody = CTBody.Factory.parse(prefix+mainPart+addPart+sufix);
	    rootBody.set(makeBody);
    }

}
